#!/usr/bin/python2.4
#
#Copyright 2009 Google Inc.
#
#Licensed under the Apache License, Version 2.0 (the "License");
#you may not use this file except in compliance with the License.
#You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
#Unless required by applicable law or agreed to in writing, software
#distributed under the License is distributed on an "AS IS" BASIS,
#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
#See the License for the specific language governing permissions and
#limitations under the License.
#

"""Handler for uploading metadata and global data.

This module provides request handlers for uploading metadata and global data
into AppEngine db and updating metadata after verification.
"""

import cgi
import cgitb
import logging
import os
from google.appengine.ext import webapp
from google.appengine.ext.webapp.util import run_wsgi_app
from base_request_handler import BaseRequestHandler
import constants
import db_utils
import metadata_utils
import metadata_in_memory
import sorting_column_sets
import text_data_reader
import id_encryption
import digital_content_wizard_exceptions

cgitb.enable()
  # Windows needs stdio set for binary mode.
try:
  import msvcrt
  #stdin = 0
  msvcrt.setmode(0, os.O_BINARY)
  # stdout = 1
  msvcrt.setmode(1, os.O_BINARY)
except ImportError:
  logging.warning('unable to find module msvcrt')


class GetMetadataHandler(BaseRequestHandler):
  """Request handler for '/getmetadata' request.

    This class handles '/getmetadata' request where it first reads the uploaded
    metadata file in memory metadata_in_memory object. It uses ReadMetadataFile
    API of text_data_reader module. It then redirects to '/uploadmetadata'
    which in turn writes metadata to db iteratively.
  """

  def post(self):
    """POST method for this request handler."""
    form = cgi.FieldStorage()
    file_item = form['file']
    logging.debug('Uploader trying to read metadata file ' + file_item.filename)
    if file_item.file:
      db_handler = db_utils.DbHandler()
      version_id = db_handler.GetLatestVersion()
      try:
        count = text_data_reader.ReadMetadataFile(file_item.file, version_id)
        logging.debug('lines read:' + str(count))
        template_values = {}
        template_values['redirect_url'] = '/updatemetadata'
        self.response.out.write(
            self.GenerateHtml('redirection.html', template_values))
      except digital_content_wizard_exceptions.DigitalContentWizardException, e:
        redirect_url = '/delete?version_id=' + id_encryption.Encrypt(version_id)
        logging.error(e)
        self.response.out.write(self.GenerateHtml('error.html',
                                                  {'message': str(e),
                                                   'redirect_url':
                                                   redirect_url}))


class UpdateMetadataHandler(BaseRequestHandler):
  """Updates metadata after changes suggested by user during verification.

  This class accepts changes suggested by user in metadata unique values and
  updates the pagetree metadata accordingly.
  """

  def get(self):
    """GET method for this request handler."""
    try:
      db_handler = db_utils.DbHandler()
      version_id = db_handler.GetLatestVersion()
      (level1_map, level2_map, level3_map) = metadata_utils.GetUniqueValueMaps()
      template_values = {
          'level1_values': level1_map,
          'level2_values': level2_map,
          'level3_values': level3_map}
      self.response.out.write(self.GenerateHtml('verify_metadata.html',
                                                template_values))
    except digital_content_wizard_exceptions.GlobalMemoryLostException, e:
      redirect_url = '/delete?version_id=' + id_encryption.Encrypt(version_id)
      logging.error(e)
      self.response.out.write(self.GenerateHtml('error.html',
                                                {'message': str(e),
                                                 'redirect_url':
                                                 redirect_url}))

  def post(self):
    """POST method for this request handler.

    Raises:
      GlobalMemoryLostException: An error occurred when a global instance
      maintaining unique values of sorting columns is lost.
    """
    form = cgi.FieldStorage()
    level_maps = []
    set_manager = sorting_column_sets.SetManager()
    db_handler = db_utils.DbHandler()
    try:
      for level in range(1, 4):
        current_level_values = set_manager.GetUniqueValues(level)
        if level < 3 and not current_level_values:
          # If any of level1 or level2 unique values are empty, global memory
          # instance is lost.
          raise digital_content_wizard_exceptions.GlobalMemoryLostException(
              'Global Memory Instance Lost')
        current_level_map = {}
        i = 1
        logging.info('Identifying changed values')
        logging.info('Level %d', level)
        for level_value in current_level_values:
          text_field_name = 'level' + str(level) + '_' + str(i)
          if text_field_name in form:
            content_field = form[text_field_name]
            new_val = unicode(
                content_field.value, constants.ENCODING, errors='strict')
            if new_val != level_value:
              current_level_map[level_value] = new_val
              logging.info(level_value)
              logging.info(new_val)
          i += 1
        level_maps.append(current_level_map)
      metadata_in_memory_instance = metadata_in_memory.MetadataInMemory()
      level3_changes_empty = not level_maps[2]
      level3_values_empty = set_manager.IsSetEmpty(3)
      level3_empty = level3_values_empty and level3_changes_empty
      if level3_empty:
        db_handler.UpdateNumberOfLevels(db_handler.GetLatestVersion(), 2)
      else:
        db_handler.UpdateNumberOfLevels(db_handler.GetLatestVersion(), 3)
      metadata_in_memory_instance.UpdateMetadata(level_maps[0], level_maps[1],
                                                 level_maps[2])
      set_manager.Clear()
      template_values = {}
      template_values['redirect_url'] = '/uploadmetadata'
      self.response.out.write(self.GenerateHtml('redirection.html',
                                                template_values))
    except digital_content_wizard_exceptions.GlobalMemoryLostException, e:
      version_id = db_handler.GetLatestVersion()
      redirect_url = '/delete?version_id=' + id_encryption.Encrypt(version_id)
      logging.error(e)
      self.response.out.write(self.GenerateHtml('error.html',
                                                {'message': str(e),
                                                 'redirect_url':
                                                 redirect_url}))


class UploadMetadataHandler(BaseRequestHandler):
  """Request Handler for uploading metadata from metadata_in_memory to db.

    This class iteratively uploads metadata to db. It first checks if
    metadata_in_memory is empty. If yes, redirects to next step in version
    creation.
    If not empty, it uses PopulateMetatadataInDb API to upload metadata in
    memory in batches.
  """

  def get(self):
    """GET method for this request handler.

    Raises:
      GlobalMemoryLostException: An error occurred when a global instance
      maintaining in-memory metadata is lost.
    """
    try:
      logging.debug('uploading metadata')
      metadata_in_memory_instance = metadata_in_memory.MetadataInMemory()
      status = metadata_in_memory_instance.IsEmpty()
      template_values = {}
      if status:
        # Metadata lost.
        raise digital_content_wizard_exceptions.GlobalMemoryLostException(
            'Metadata In Memory Instance Lost.')
      else:
        count = metadata_in_memory_instance.PopulateMetadataInDb()
        logging.debug('UploadMetadataHandler: rows uploaded:' + str(count))
        status = metadata_in_memory_instance.IsEmpty()
        if status:
          prefix_url = '/create?status='
          redirect_url = prefix_url + constants.GET_GLOBALDATA
          template_values['redirect_url'] = redirect_url
        else:
          template_values['redirect_url'] = '/uploadmetadata'
      self.response.out.write(self.GenerateHtml('redirection.html',
                                                template_values))
    except digital_content_wizard_exceptions.DigitalContentWizardException, e:
      db_handler = db_utils.DbHandler()
      version_id = db_handler.GetLatestVersion()
      redirect_url = '/delete?version_id=' + id_encryption.Encrypt(version_id)
      logging.error(e)
      self.response.out.write(self.GenerateHtml('error.html',
                                                {'message': str(e),
                                                 'redirect_url':
                                                 redirect_url}))


class GlobalDataHandler(BaseRequestHandler):
  """Request Handler to read global data.

  It first reads the uploaded global data and uploads it to db using
  GetGlobalData API of text_data_reader module.
  """

  def post(self):
    """POST method for this request handler."""
    form = cgi.FieldStorage()
    file_item = form['file']
    logging.debug(
        'Uploader trying to read global metadata file ' + file_item.filename)
    if file_item.file:
      try:
        db_handler = db_utils.DbHandler()
        version_id = db_handler.GetLatestVersion()
        text_data_reader.GetGlobalData(file_item.file, version_id)
        self.redirect('/create?status=' + constants.IMAGE_UPLOAD)
        return
      except digital_content_wizard_exceptions.InvalidGlobaldataException, e:
        redirect_url = '/delete?version_id=' + id_encryption.Encrypt(version_id)
        logging.error(e)
        self.response.out.write(self.GenerateHtml('error.html',
                                                  {'message': str(e),
                                                   'redirect_url':
                                                   redirect_url}))


application = webapp.WSGIApplication(
    [('/updatemetadata', UpdateMetadataHandler),
     ('/getglobal', GlobalDataHandler),
     ('/uploadmetadata', UploadMetadataHandler),
     ('/getmetadata', GetMetadataHandler)],
    debug=True)


def main():
  run_wsgi_app(application)


if __name__ == '__main__':
  main()
